<cfcomponent>

	<cffunction name="Init" access="public" output="no" hint="Initialises the component">
    	<cfargument name="configFilePath" type="string" required="no" default="#GetDirectoryFromPath(GetCurrentTemplatePath())#feedconfig.xml.cfm" hint="The full filename/path to the config file for the component, defaults to feedconfig.xml.cfm in the same folder as the CFC">
    	<cfset VARIABLES.configFilePath = ARGUMENTS.configFilePath>
		<!--- this will store the feed info of each feed --->
		<cfset VARIABLES.feedQueryArray = ArrayNew("1")>
		<!--- this will store the current feed info --->
		<cfset VARIABLES.allFeedsQuery = QueryNew("nothing")>
        <cfset VARIABLES.LockName="lck_" & CreateUUID()>
        <cfreturn THIS>
    </cffunction> 
    
    <cffunction name="GetFilePath" output="no" access="public" returntype="string">
    	<cfreturn VARIABLES.configFilePath>
    </cffunction>
    
    <cffunction name="GetAllFeedsQuery" output="no" access="public" returntype="query" hint="Returns the all feeds query">
    	<cfreturn VARIABLES.allFeedsQuery>
    </cffunction>
    
    <cffunction name="GetFeedQueryArray" output="no" access="public" returntype="array" hint="Returns the feeds array">
    	<cfreturn VARIABLES.feedQueryArray>
    </cffunction>

    <cffunction name="DoPing" output="no" access="public" returntype="boolean" hint="Pings a given feed and adds its query to the feed data">
    	<cfargument name="FeedID" required="yes" type="numeric" hint="The ID of the feed to add">
        <cfset var configXML = XMLParse(VARIABLES.configFilePath)>
        <cfset var thisFeed = XmlSearch(configXML,"//feed[@id='#ARGUMENTS.FeedID#']")>
        <cfset var feedURL = thisFeed[1].url.XMLText>
        <cfset var feedName = thisFeed[1].blogname.XMLText>
        <cfset var feedquery = GetFeed(feedURL,feedName)>
        <cflock type="exclusive" name="#VARIABLES.LockName#" timeout="120">
			<cfset VARIABLES.feedQueryArray[ARGUMENTS.FeedID] = feedquery>
            <cfset CombineFeeds()>
        </cflock>
        <cfreturn TRUE>
    </cffunction>
    
    <cffunction name="DoAllPings" output="no" access="public" returntype="boolean" hint="Pings all feeds and adds the queries to the feed data">
        <cfset var feedURL = "">
        <cfset var feedName = "">
        <cfset var feedquery = "">
		<cfset var configXML = XMLParse(VARIABLES.configFilePath)>
        <cfset var allFeeds = XmlSearch(configXML,"//feed")>
        <cfset var i = 1>
        <cflock type="exclusive" name="#VARIABLES.LockName#" timeout="120">
            <cfloop condition="i LTE ArrayLen(allFeeds)">
                <cfset feedURL = allFeeds[i].url.XMLText>
                <cfset feedName = allFeeds[i].blogname.XMLText>
                <cfset feedquery = GetFeed(feedURL,feedName)>
                <cfset VARIABLES.feedQueryArray[i] = feedquery>
                <cfset i = i + 1>
            </cfloop>
            <cfset CombineFeeds()>
        </cflock>
        <cfreturn TRUE>
    </cffunction>

	<cffunction name="XmlToQuery" output="false" access="private" returntype="query">
		<cfargument name="xmlPacket" type="string" required="yes">
		
		<cfset var xmlColumnList = "">
        <cfset var xmlColumnTypeList = "">
        <cfset var xmlColumnTypes = StructNew()>
        <cfset var xmlSize = "">
        <cfset var noColumns = "">
        <cfset var column = "">
        <cfset var i = "">
        <cfset var a = "">
        <cfset var b = "">
        <cfset var thisFeedType = "">
        <cfset var theElementsWeNeed = "">
        <cfset var thisColumnType = "">
        <cfset var thisColumnName = "">
		
		<!--- Create XML struct --->
		<cfif NOT IsXMLDoc(arguments.xmlPacket)>
			<cfset arguments.xmlPacket = XmlParse(arguments.xmlPacket)>
		</cfif>
        
        <!--- now get the version --->
        
        <cfset thisFeedType = GetFeedType(arguments.xmlPacket)>
        
        <cfset theElementsWeNeed = xmlSearch(arguments.xmlPacket,thisFeedType.XPath)>
		
		<!--- No of rows --->
        <cfset xmlSize = ArrayLen(theElementsWeNeed)>
        
        <!--- Create XML columns list --->
        <cfset xmlColumnList = thisFeedType.Columns>
		<cfloop list="#xmlColumnList#" index="thisColumnName">
			<cfif ListContains(thisFeedType.DateColumns,thisColumnName)>
                <cfset xmlColumnTypeList = ListAppend(xmlColumnTypeList,"date")>
            <cfelse>
                <cfset xmlColumnTypeList = ListAppend(xmlColumnTypeList,"varchar")>
            </cfif>
		</cfloop>
        
		<!--- Create query with approp no of rows --->
		<cfset qXML = QueryNew(xmlColumnList,xmlColumnTypeList)>
		<cfset QueryAddRow(qXML,xmlsize)>
		
		<!--- Build query --->
		<cfloop from="1" to="#xmlSize#" step="1" index="a">
			<cfloop list="#xmlColumnList#" index="b">
				<cftry>
					<cfset cElement = theElementsWeNeed[a][b].xmlText>
                    <cfif ListContains(thisFeedType.DateColumns,LCase(b))>
                    	<cfset cElement = parseDateTime(cElement)>
                    </cfif>
					<cfset QuerySetCell(qXML,b,cElement,a)>
					<cfcatch type="expression">
						<!--- NULL DATA COLUMN SO DO NOT INSERT INTO QUERY & CONTINUE--->
					</cfcatch>
				</cftry>
			</cfloop>
		</cfloop>
			
		<cfreturn qXML>
	</cffunction>
    
    <cffunction name="GetFeedType" output="no" access="private" returntype="struct" hint="Returns the type of feed and the necessary elements to parse">
    	<cfargument name="xmlPacket" type="string" required="yes">
        <!--- etc --->
        <cfset var thisFeedType = xmlPacket.XmlRoot.xmlName>
        <cfset var feedReturn = StructNew()>
        <!--- this bit eventually needs to return column mappings, a list of columns to transform to dates, etc for all feed types --->
        <cfswitch expression="#thisFeedType#">
        	<cfcase value="rss">
            	<cfset feedReturn.FeedType = "RSS">
                <cfset feedReturn.XPath = "//*[local-name() = 'item']">
                <cfset feedReturn.Columns = "description,link,pubdate,title">
				<cfset feedReturn.DateColumns = "pubdate">
            </cfcase>
            <cfcase value="feed">
            	<cfset feedReturn.FeedType = "ATOM">
                <cfset feedReturn.XPath = "//*[local-name() = 'entry']">
            </cfcase>
            <cfdefaultcase>
            	<cfset feedReturn.FeedType = "">
            </cfdefaultcase>
        </cfswitch>
        <cfreturn feedReturn>
    </cffunction>
    
    <cffunction name="GetFeed" output="no" access="private" returntype="query" hint="Fetches a feed, parses it and returns it as a query">
		<cfargument name="feedURL" type="string" required="yes" hint="The URL of the feed to fetch">
        <cfargument name="feedName" type="string" required="yes" hint="The name to give this feed in the returned query">
		<cfset var cfhttp = "">
		<cfset var xmlContent = "">
        <cfset var qXML = "">
        <cfset blogNameArray = ArrayNew(1)>
<cftry>
		<!--- <cfhttp url="#ARGUMENTS.feedURL#" method="GET" timeout="20" proxyserver="192.168.2.244" proxyport="3128" proxyuser="REIWA\andrewm" proxypassword="reiwa06^" throwonerror="true" /> --->
		<cfhttp url="#ARGUMENTS.feedURL#" method="GET" timeout="20" throwonerror="true" />
		<cfset xmlContent = Trim(cfhttp.filecontent) />
		<cfset xmlContent = XMLParse(xmlContent) />
		<cfset qXML = XmlToQuery(xmlContent)>
<cfcatch >
<!--- <cfdump var="#ARGUMENTS.feedURL#" label="ARGUMENTS.feedURL"><br />
<cfdump var="#cfhttp#" label="cfhttp">	
<cfdump var="#CFCATCH#" label="error">
<cfabort showerror="rss_agg.cfc - getFeed"> --->
</cfcatch>
</cftry>				
        <cfset ArraySet(blogNameArray,1,qXML.RecordCount,ARGUMENTS.feedName)>
        <cfset QueryAddColumn(qXML,"blogname","VARCHAR",blogNameArray)>
        <cfreturn qXML>
    </cffunction>
    
    <cffunction name="CombineFeeds" output="no" access="private" returntype="boolean" hint="Combines all the stored feed queries into an aggregated feed">
    	<cfset var QFeeds  = "">
        <cfset var i = 1>
        <cfset var thisQuery = StructNew()>
        <cfquery dbtype="query" name="QFeeds">
        	(<cfloop condition="i LTE ArrayLen(VARIABLES.feedQueryArray)">
            	<cfset thisquery["num_" & i] = VARIABLES.feedQueryArray[i]>
                SELECT * 
                  FROM thisQuery.num_#i#
                <cfif i NEQ ArrayLen(VARIABLES.feedQueryArray)>
                 UNION
				</cfif>
                <cfset i = i + 1>
            </cfloop>)
            ORDER BY PUBDATE DESC
        </cfquery>
        <cfset VARIABLES.allFeedsQuery = QFeeds>
        <cfreturn TRUE>
    </cffunction>


</cfcomponent>
